#include "rewine/impl/CDllsoImage.hpp"

#include "dllso/DllsoInit.hpp"
#include "dso/DsoLoader.hpp"
#include "misc/fs_ext.hpp"
#include "misc/string_ext.hpp"
#include "rewine/LoggerSingleton.hpp"

namespace rewine {

int CDllsoImage::init(const std::string &fullname) {
    auto filename = get_filename(fullname);
    lower_string(filename);
    if (!ends_with(filename, ".dll.so")) {
        return -1;
    }
    auto exportName = filename.substr(0, filename.length() - 3);
    setFullName(fullname);
    setExportName(exportName);
    return 0;
}

static void
CPeImage_deleter(CPeImage *pImage) {
    pImage->unload();
    delete pImage;
}

int CDllsoImage::preload() {
    m_Handle = rewine_dlopen(fullname().c_str(), false, true);
    if (!m_Handle) {
        g_Logger->error(__FLF__, "dlopen: {}", rewine_dlerror());
        return -1;
    }

    int ret;

    const char *filename;
    void *dos;
    dllso::fetchLastDllso(&filename, &dos);
    if (!dos) {
        //TODO: retry as CDsoImage
        return -2;
    }
    auto pMap = std::make_shared<MapIndicator>(nullptr, dos, 0, SIZE_MAX);

    m_PeImage.reset(new CPeImage(), &CPeImage_deleter);
    ret = m_PeImage->initFromMap(pMap, true);
    if (ret < 0) {
        g_Logger->error(__FLF__, "{} initFromMap ret={}", fullname().c_str(), ret);
        return -3;
    }

    ret = m_PeImage->parse();
    if (ret < 0) {
        g_Logger->error(__FLF__, "{} parse ret={}", fullname().c_str(), ret);
        return -4;
    }

    return 0;
}

int CDllsoImage::load() {
    int ret;

    ret = m_PeImage->load();
    if (ret < 0) {
        return -1;
    }

    return 0;
}

int CDllsoImage::unload() {
    m_PeImage.reset();
    return 0;
}

int
CDllsoImage::getExportCount() const {
    return m_PeImage->getExportCount();
}

IExport *
CDllsoImage::getExportByIndex(unsigned int index) const {
    return m_PeImage->getExportByIndex(index);
}

IExport *
CDllsoImage::getExportByName(const std::string &name, uint16_t hint) const {
    return m_PeImage->getExportByName(name);
}

void *
CDllsoImage::getSymbolInDSO(const char *name) const {
    return rewine_dlsym(*m_Handle, name);
}

} // namespace rewine
